﻿using System;
using System.Runtime.Remoting.Contexts;
using Autofac;
using Autofac.Core;
using ReactiveGraph.Core;
using ReactiveGraph.Core.Configuration.Containers;

namespace ReactiveGraph.Object.Configuration.Containers
{
    public class AutofacObjectModule  : Module
    {

        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterModule<AutofacCoreModule>();
            builder.Register<Func<string, Type, IProperty>>(
                c =>
                    {
                        var ctx = c.Resolve<IComponentContext>();
                        return ((name, type) => GetProperty(type, name, ctx));
                    }
                );

            builder.Register<Func<string, Type, IReactiveNode>>
                (c =>
                     {
                         var ctx = c.Resolve<IComponentContext>();
                         return ((name, type) => GetNode(type, name, ctx));
                     })
                .SingleInstance();

            builder.Register<Func<Type, IReactiveNode>>
                (c =>
                     {
                         var ctx = c.Resolve<IComponentContext>();
                         return (type => (IReactiveNode) ctx.Resolve(type));
                     })
                .SingleInstance();

            builder.RegisterGeneric(typeof (Property<>));
            builder.RegisterType<Context>();
            builder.RegisterType<NodeFactories>().SingleInstance();
            builder.RegisterAssemblyTypes(AppDomain.CurrentDomain.GetAssemblies())
                .AssignableTo<IReactiveObject>()
                .AsSelf()
                .OnActivating
                (args =>
                     {
                         var reactiveObject = args.Instance as IReactiveObject;
                         reactiveObject.SetContext(args.Context.Resolve<Context>());
                         reactiveObject.Initialize();
                     });
}

        private static IReactiveNode GetNode(Type t, string name, IComponentContext componentContext)
        {
            var nodeNameService = componentContext.Resolve<NodeNameService>();
            nodeNameService.OverrideName(name);
            var resolved = componentContext.Resolve(t);
            nodeNameService.ClearOverride();
            return (IReactiveNode)resolved;
        }

        private static IProperty GetProperty(Type propertyType, string name, IComponentContext componentContext)
        {
            if (!typeof(IProperty).IsAssignableFrom(propertyType))
                throw new ArgumentException("SHould be IProperty", "propertyType");
            object o;
            var typedService = new TypedService(propertyType);
            var typedParameter = new TypedParameter(typeof(string), name);
            componentContext.TryResolveService(typedService, new[] { typedParameter }, out o);
            return (IProperty)o;
        }


    }
}
